#!/usr/bin/env python3
"""
代码审查MCP工具
提供基于分支的代码审查功能
"""

import os
import asyncio
from typing import Dict, Any
from urllib.parse import urlparse

from gitlab import Gitlab
from biz.llm.factory import Factory
from biz.utils.token_util import count_tokens, truncate_text_by_tokens
from biz.utils.code_parser import GitDiffParser


class CodeReviewTool:
    """
    代码审查工具
    基于GitLab分支进行代码审查
    """
    
    def __init__(self):
        self.client = None
        self.review_max_tokens = int(os.getenv('REVIEW_MAX_TOKENS', 10000))
    
    def _get_client(self):
        """延迟初始化LLM客户端"""
        if self.client is None:
            self.client = Factory().getClient()
        return self.client
    
    def parse_gitlab_url(self, url: str) -> tuple[str, str]:
        """
        解析GitLab URL，提取gitlab_url和project_id
        """
        parsed_url = urlparse(url)
        
        if not parsed_url.scheme or not parsed_url.netloc:
            raise ValueError("无效的GitLab项目URL，请确保URL格式正确")
        
        gitlab_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
        
        # 提取项目路径
        path = parsed_url.path.strip('/')
        if path.endswith('.git'):
            path = path[:-4]
        
        return gitlab_url, path
    
    def get_branch_changes(self, gitlab_url: str, project_id: str, branch_name: str, access_token: str = None) -> str:
        """
        获取分支的代码变更
        """
        token = access_token or os.getenv("GITLAB_ACCESS_TOKEN")
        if not token:
            raise ValueError("未提供GitLab访问令牌，请设置GITLAB_ACCESS_TOKEN环境变量或传入access_token参数")
        
        try:
            gl = Gitlab(gitlab_url, private_token=token)
            project = gl.projects.get(project_id, lazy=True)
            
            # 获取分支信息
            try:
                branch = project.branches.get(branch_name)
            except Exception:
                raise ValueError(f"分支 '{branch_name}' 不存在")
            
            # 获取分支与主分支的差异
            try:
                # 尝试与main分支比较
                compare = project.repository_compare('main', branch_name)
            except Exception:
                try:
                    # 如果main不存在，尝试与master分支比较
                    compare = project.repository_compare('master', branch_name)
                except Exception:
                    raise ValueError("无法找到主分支（main或master）进行比较")
            
            if not compare['diffs']:
                return "该分支与主分支没有差异"
            
            changes_text = f"分支 '{branch_name}' 的代码变更:\n\n"
            
            for diff in compare['diffs']:
                file_path = diff['new_path'] or diff['old_path']
                changes_text += f"\n### 文件: {file_path}\n"
                
                if diff['new_file']:
                    changes_text += "状态: 新增文件\n"
                elif diff['deleted_file']:
                    changes_text += "状态: 删除文件\n"
                elif diff['renamed_file']:
                    changes_text += f"状态: 重命名文件 ({diff['old_path']} -> {diff['new_path']})\n"
                else:
                    changes_text += "状态: 修改文件\n"
                
                if diff['diff']:
                    changes_text += f"\n```diff\n{diff['diff']}\n```\n"
            
            return changes_text
            
        except Exception as e:
            raise ValueError(f"获取分支变更失败: {str(e)}")
    
    def get_review_prompts(self, changes_text: str) -> list[dict[str, str]]:
        """
        生成代码审查的提示词
        """
        system_prompt = """
你是一位资深的代码审查专家，请对提供的代码变更进行全面审查。

### 审查要点：
1. **功能正确性**: 代码逻辑是否正确，是否能实现预期功能
2. **安全性**: 是否存在安全漏洞或风险
3. **最佳实践**: 是否遵循编程最佳实践和代码规范
4. **性能**: 是否存在性能问题或优化空间
5. **可维护性**: 代码是否易于理解和维护
6. **测试覆盖**: 是否需要添加或修改测试

### 输出格式：
请按照以下Markdown格式输出审查结果：

## 代码审查报告

### 📋 概述
[简要描述本次变更的主要内容]

### ✅ 优点
[列出代码的优点和好的实践]

### ⚠️ 问题和建议
[详细列出发现的问题，按严重程度分类]

#### 🔴 严重问题
[需要立即修复的问题]

#### 🟡 改进建议
[可以优化的地方]

#### 🔵 最佳实践建议
[代码规范和最佳实践相关的建议]

### 📊 评分
- 功能正确性: X/10
- 安全性: X/10
- 代码质量: X/10
- 性能: X/10
- 可维护性: X/10

**总分: XX/50**

### 🎯 总结
[总体评价和主要改进方向]
"""
        
        user_prompt = f"""
请审查以下代码变更：

{changes_text}
"""
        
        return [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_prompt}
        ]
    
    async def execute(self, arguments: Dict[str, Any]) -> str:
        """
        执行代码审查
        """
        try:
            gitlab_url = arguments.get("gitlab_url")
            branch_name = arguments.get("branch_name")
            access_token = arguments.get("access_token")
            
            if not gitlab_url or not branch_name:
                return "错误: 缺少必需参数 gitlab_url 或 branch_name"
            
            # 解析GitLab URL
            parsed_gitlab_url, project_id = self.parse_gitlab_url(gitlab_url)
            
            # 获取分支变更
            changes_text = self.get_branch_changes(parsed_gitlab_url, project_id, branch_name, access_token)
            
            if not changes_text or changes_text.strip() == "该分支与主分支没有差异":
                return "该分支与主分支没有差异，无需审查"
            
            # Token数量检查和截断
            tokens_count = count_tokens(changes_text)
            if tokens_count > self.review_max_tokens:
                changes_text = truncate_text_by_tokens(changes_text, self.review_max_tokens)
            
            # 生成提示词并调用LLM
            messages = self.get_review_prompts(changes_text)
            
            # 异步调用LLM（如果LLM客户端支持异步）
            loop = asyncio.get_event_loop()
            client = self._get_client()
            review_result = await loop.run_in_executor(
                None, 
                client.completions, 
                messages
            )
            
            # 清理结果格式
            if review_result.startswith("```markdown") and review_result.endswith("```"):
                review_result = review_result[11:-3].strip()
            
            return review_result
            
        except Exception as e:
            return f"代码审查执行失败: {str(e)}"